/******************************************************************************/
/*  mb_func_smartbug_cmd.c                                                    */
/******************************************************************************/

#include "data_types.h"
#include "Core\core.h"
#include "Rtc\rtc.h"
#include "Device\device.h"
#include "Temp\temp.h"
#include "Fmem_layer\fmem_layer.h"
#include "Updater\updater.h"
#include "Log\log.h"
#include "Mlog\mlog.h"
#include "TNKernel\tn.h"

#include "port\port.h"
#include "mbconfig.h"
#include "mb.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbproto.h"
#include "functions\mbutils.h"
#include "mbfunc.h"




/******************************************************************************/
/*      Defines                                                               */
/******************************************************************************/
#if MB_FUNC_SMARTBUG_CMD_ENABLED > 0
typedef enum
{
    MB_SM_RESET = 0,
    MB_SM_SET_TIME,
    MB_SM_SET_DATE,
    MB_SM_RD_LOG,
    MB_SM_WR_FW,
    MB_SM_RD_FW,
    MB_SM_RUN_UPDATE,
    MB_SM_SEARCH_TEMP_SENS,
    MB_SM_CLEAR_LOG
} __MB_SM_CMD;


eMB_EXCEPTION prveMbError2Exception(const eMB_ERROR_CODE _errorCode);




//==============================================================================
// MbFuncSmartbugCmd()
//==============================================================================
eMB_EXCEPTION MbFuncSmartbugCmd(uint8_t * _pFrame, uint16_t * _pLen)
{
    /* _pFrame[MB_PDU_DATA_OFF]       - cmd id
     * _pFrame[MB_PDU_DATA_OFF + 1].. - cmd data
     */

    // Test
    if ((_pFrame == 0) || ((*_pLen) < 2)) {
        *_pLen = 1;
        return prveMbError2Exception(MB_EINVAL);
    };

    const uint32_t len = (*_pLen) - 2;
    eMB_ERROR_CODE err = MB_EINVAL;

    // Process cmd
    *_pLen = 1;
    switch (_pFrame[MB_PDU_DATA_OFF])
    {
        //----------------------------------------------------------------------
        case MB_SM_RESET:
          if (!len) {
              CORE::ResetDevice();
              err = MB_ENOERR;
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_SET_TIME:
          if (len == 3) {
              if ((_pFrame[MB_PDU_DATA_OFF + 1] < 24)
                  && (_pFrame[MB_PDU_DATA_OFF + 2] < 60)
                      && (_pFrame[MB_PDU_DATA_OFF + 3] < 60)) {

                  RTC::StopRtc();

                  RTC::SetHour(_pFrame[MB_PDU_DATA_OFF + 1]);
                  RTC::SetMin(_pFrame[MB_PDU_DATA_OFF + 2]);
                  RTC::SetSec(_pFrame[MB_PDU_DATA_OFF + 3]);

                  RTC::StartRtc();
                  err = MB_ENOERR;
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_SET_DATE:
          if (len == 3) {
              if (((_pFrame[MB_PDU_DATA_OFF + 1] < 32) && (_pFrame[MB_PDU_DATA_OFF + 1] > 0))
                  && ((_pFrame[MB_PDU_DATA_OFF + 2] < 13) && (_pFrame[MB_PDU_DATA_OFF + 2] > 0))) {

                  RTC::StopRtc();

                  RTC::SetDom(_pFrame[MB_PDU_DATA_OFF + 1]);
                  RTC::SetMonth(_pFrame[MB_PDU_DATA_OFF + 2]);
                  RTC::SetYear(2000 + _pFrame[MB_PDU_DATA_OFF + 3]);
                  RTC::StartRtc();

                  err = MB_ENOERR;
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_RD_LOG:
          /*|ind=1|dot num=4 */
          if (len == 5) {
              LOG * pLog;
              uint8_t ind;
              __UNION_DWORD dot;

              // Read data
              ind = _pFrame[MB_PDU_DATA_OFF + 1];

              dot.data8[0] = _pFrame[MB_PDU_DATA_OFF + 2];
              dot.data8[1] = _pFrame[MB_PDU_DATA_OFF + 3];
              dot.data8[2] = _pFrame[MB_PDU_DATA_OFF + 4];
              dot.data8[3] = _pFrame[MB_PDU_DATA_OFF + 5];

              pLog = (LOG *)MLOG::GetPntrLog(ind);
              if (pLog != 0) {
                  _pFrame[MB_PDU_DATA_OFF] = pLog->ReadRec(dot.data32, _pFrame + 2);

                  if (_pFrame[MB_PDU_DATA_OFF] != 0) {
                      *_pLen += _pFrame[MB_PDU_DATA_OFF] + 1;
                      err = MB_ENOERR;
                  };
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_WR_FW:
          if (len > 4) {
              __FMEM_DATA * pMem;
              __UNION_DWORD tn_err;

              // Allocate mem block
              tn_err.sdata32 = tn_fmem_get_polling((TN_FMP *)DEVICE::Get_mem_pool((len - 4) + sizeof(__FMEM_DATA)),
                                                   (void **)&pMem);

              if (tn_err.sdata32 == TERR_NO_ERR) {
                  // Send signal
                  pMem->pBuff = ((uint8_t *)pMem) + sizeof(__FMEM_DATA);
                  pMem->dataLen = len - 4;
                  memcpy(pMem->pBuff, _pFrame + MB_PDU_DATA_OFF + 5, pMem->dataLen);

                  tn_err.data8[0] = _pFrame[MB_PDU_DATA_OFF + 1];
                  tn_err.data8[1] = _pFrame[MB_PDU_DATA_OFF + 2];
                  tn_err.data8[2] = _pFrame[MB_PDU_DATA_OFF + 3];
                  tn_err.data8[3] = _pFrame[MB_PDU_DATA_OFF + 4];

                  pMem->addr = tn_err.data32;

                  tn_err.sdata32 = tn_queue_send_polling((TN_DQUE *)UPDATER::Get_updater_dque(),
                                                         pMem);

                  if (tn_err.sdata32 == TERR_NO_ERR) {
                      err = MB_ACKNOWLEDGE;
                  }
                  else {
                      // Release memory
                      tn_err.sdata32 = tn_fmem_release((TN_FMP *)DEVICE::Get_mem_pool((len - 4) + sizeof(__FMEM_DATA)),
                                                       pMem);
                  };
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_RD_FW:
          if (len == 5) {
              __FMEM_DATA mem;
              __UNION_DWORD mem_err;

              // Read data
              mem_err.data8[0] = _pFrame[MB_PDU_DATA_OFF + 1];
              mem_err.data8[1] = _pFrame[MB_PDU_DATA_OFF + 2];
              mem_err.data8[2] = _pFrame[MB_PDU_DATA_OFF + 3];
              mem_err.data8[3] = _pFrame[MB_PDU_DATA_OFF + 4];

              mem.addr = mem_err.data32;
              mem.pBuff = _pFrame + 2;
              mem.dataLen = _pFrame[MB_PDU_DATA_OFF + 5];

              mem_err.data32 = UPDATER::ReadFwMem(&mem);

              if (mem_err.data32 == eMEM_OK) {
                  _pFrame[MB_PDU_DATA_OFF] = mem.dataLen;
                  *_pLen += mem.dataLen + 1;
                  err = MB_ENOERR;
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_RUN_UPDATE:
          if (len == 8) {
              __UNION_DWORD mem_err;

              mem_err.data8[0] = _pFrame[MB_PDU_DATA_OFF + 1];
              mem_err.data8[1] = _pFrame[MB_PDU_DATA_OFF + 2];
              mem_err.data8[2] = _pFrame[MB_PDU_DATA_OFF + 3];
              mem_err.data8[3] = _pFrame[MB_PDU_DATA_OFF + 4];

              const uint32_t fwSize = mem_err.data32;

              mem_err.data8[0] = _pFrame[MB_PDU_DATA_OFF + 5];
              mem_err.data8[1] = _pFrame[MB_PDU_DATA_OFF + 6];
              mem_err.data8[2] = _pFrame[MB_PDU_DATA_OFF + 7];
              mem_err.data8[3] = _pFrame[MB_PDU_DATA_OFF + 8];

              const uint32_t fwCrc = mem_err.data32;
              mem_err.data32 = UPDATER::RunUpdate(fwSize, fwCrc, 0);

              if (mem_err.data32 == eMEM_OK) {
                  err = MB_ENOERR;
              };
          };
        break;

        //----------------------------------------------------------------------
        case MB_SM_SEARCH_TEMP_SENS:
          TEMP::SetSignalNeedSearchSensors();
          err = MB_ENOERR;
        break;

        //----------------------------------------------------------------------
        case MB_SM_CLEAR_LOG:
          /*|ind=1|*/
          if (len == 1) {
              LOG * pLog;
              uint8_t ind;

              // Read data
              ind = _pFrame[MB_PDU_DATA_OFF + 1];

              pLog = (LOG *)MLOG::GetPntrLog(ind);
              if (pLog != 0) {
                  pLog->ClearLog();
              };
          };
          err = MB_ENOERR;
        break;

        //----------------------------------------------------------------------
        default:
        break;
    };

    return prveMbError2Exception(err);
}
//==============================================================================
//==============================================================================
#endif